﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Gmantis.Controls
{
    public class ScrollHelper
    {
        // Fields
        private bool _avoidIfCtrlPressed = true;
        private bool _continuousScroll = true;
        private double _cumulativeScroll;
        private UIElement _element;
        private System.Windows.RoutedEventArgs _firstEventArgs;
        private bool _handleMouseWheel = true;
        private PointerDeviceType _pointerType;
        private bool _scrollStarted;
        private double _targetScroll;

        // Events
        public event EventHandler<ScrollCompletedEventArgs> ScrollCompleted;

        public event EventHandler<ScrollDeltaEventArgs> ScrollDelta;

        public event EventHandler<ScrollStartedEventArgs> ScrollStarted;

        // Methods
        public ScrollHelper(UIElement element, bool avoidIfCtrlPressed = true, bool continuousScroll = true, bool handleMouseWheel = true)
        {
            this._element = element;
            this._handleMouseWheel = handleMouseWheel;
            this._continuousScroll = continuousScroll;
            this._avoidIfCtrlPressed = avoidIfCtrlPressed;
            this.InitializeScroll();
        }

        internal void Complete(System.Windows.RoutedEventArgs originalArgs = null)
        {
            if (this._scrollStarted)
            {
                this._scrollStarted = false;
                if (this._continuousScroll)
                {
                    CompositionTarget.Rendering -= new EventHandler(this.OnRendering);
                }
                this.RaiseScrollCompleted(originalArgs, this._cumulativeScroll);
            }
        }

        public void FinalizeScroll()
        {
            this._element.MouseWheel -= new MouseWheelEventHandler(this.OnMouseWheel);
        }

        private void InitializeScroll()
        {
            this._scrollStarted = false;
            this._element.MouseWheel += new MouseWheelEventHandler(this.OnMouseWheel);
        }

        private void OnMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
        {
            if (!this._avoidIfCtrlPressed || !KeyboardUtil.Ctrl)
            {
                if (!this._scrollStarted)
                {
                    this.Start(e);
                }
                double delta = e.Delta;
                if (this._continuousScroll)
                {
                    this._targetScroll += delta;
                }
                else
                {
                    this._cumulativeScroll += delta;
                    this.RaiseScrollDelta(e, this._cumulativeScroll, delta);
                    this.Complete(e);
                }
                if (this._handleMouseWheel)
                {
                    e.Handled = true;
                }
            }
        }

        private void OnRendering(object sender, EventArgs e)
        {
            double deltaScroll = (this._targetScroll - this._cumulativeScroll) / 10.0;
            this._cumulativeScroll += deltaScroll;
            this.RaiseScrollDelta(this._firstEventArgs, this._cumulativeScroll, deltaScroll);
            if (Math.Abs(deltaScroll) < 0.1)
            {
                this.Complete(null);
            }
        }

        private void RaiseScrollCompleted(System.Windows.RoutedEventArgs originalArgs, double cumulativeScroll)
        {
            if (this.ScrollCompleted != null)
            {
                this.ScrollCompleted(this._element, new ScrollCompletedEventArgs(this, originalArgs, this._pointerType, cumulativeScroll));
            }
        }

        private void RaiseScrollDelta(System.Windows.RoutedEventArgs originalArgs, double cumulativeScroll, double deltaScroll)
        {
            if (this.ScrollDelta != null)
            {
                this.ScrollDelta(this._element, new ScrollDeltaEventArgs(this, originalArgs, this._pointerType, deltaScroll, cumulativeScroll));
            }
        }

        private void RaiseScrollStarted(System.Windows.RoutedEventArgs originalArgs)
        {
            if (this.ScrollStarted != null)
            {
                this.ScrollStarted(this._element, new ScrollStartedEventArgs(this, originalArgs, this._pointerType));
            }
        }

        private void Start(System.Windows.RoutedEventArgs originalArgs)
        {
            if (this._scrollStarted)
            {
                this.Complete(originalArgs);
            }
            this._cumulativeScroll = 0.0;
            this._targetScroll = 0.0;
            this._scrollStarted = true;
            this._pointerType = InputEventArgs.GetPointerType(originalArgs);
            this._firstEventArgs = originalArgs;
            this.RaiseScrollStarted(originalArgs);
            if (this._continuousScroll)
            {
                CompositionTarget.Rendering += new EventHandler(this.OnRendering);
            }
        }
    }
}
